/* External definitions for single-server queueing system. */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>  /* Header file for random-number generator. */

#define Q_LIMIT 100  /* Limit on queue length. */
#define BUSY      1  /* Mnemonics for server's being busy */
#define IDLE      0  /* and idle. */
#define a (double) 16807.0
#define m (double) 2147483647.0
#define q (double) 127773.0
#define r (float) 2836.0

int   next_event_type, num_custs_delayed, num_delays_required, num_events,
num_in_q, server_status, job_gt_45, c_value, num_being_served;
float area_num_in_q, area_server_status, mean_interarrival, mean_service,
sim_time, time_arrival[Q_LIMIT + 1], time_service[Q_LIMIT+1], time_last_event, time_next_event[3],
total_of_delays, my_random, time_required, service_times[Q_LIMIT+1], time_q_gt1;
FILE  *infile, *outfile;
double hi, test, lo, *seed1, *seed2;
char service, arrival, type;

void  initialize(void);
void  timing(void);
void  arrive(void);
void  depart(void);
void  report(void);
void  update_time_avg_stats(void);
float expon(float mean, double*);
float randomvar(double*);


main()  /* Main function. */
{
	/* Specify the number of events for the timing function. */

	outfile = fopen("mm1.out", "w");
	num_events = 2;

	/* Specify the starting Seed */
	char terminating_condition;

	int done = 0;
	seed1 = malloc(sizeof(double));
	seed2 = malloc(sizeof(double));


	do
	{
		printf("Enter terminating condition time(t) or people(p)\n");
		scanf("\n%c", &terminating_condition);
		if(terminating_condition == 't')
		{
			printf("Enter terminating time in min\n");
			scanf("%f", &time_required);
			done = 1;
		}
		else if(terminating_condition == 'p')
		{
			printf("Enter terminating number of people\n");
			scanf("%d", &num_delays_required);
			time_required = -1;
			done = 1;
		}
		else
		{
			printf("Incorrect format try again");
		}
	}
	while(done != 1);
	printf("Enter c value, 0 = infinity\n");
	scanf("\n%d", &c_value);
	/* Read input parameters. */

	mean_interarrival = 5;
	mean_service = 5;
	/* Write report heading and input parameters. */
	int i;
	for(i = 0; i < 6; i++)
	{
		switch(i) {
		case 0:
		{
			service = 'M';
			arrival = 'M';
			break;
		}
		case 1:
		{
			service = 'M';
			arrival = 'D';
			break;
		}
		case 2:
		{
			service = 'M';
			arrival = 'U';
			break;
		}
		case 3:
		{
			service = 'D';
			arrival = 'D';
			break;
		}
		case 4:
		{
			service = 'U';
			arrival = 'U';
			break;
		}
		case 5:
		{
			service = 'U';
			arrival = 'M';
			break;
		}
		}
		fprintf(outfile, "%c/%c Single-server queueing system with c = %d\n\n", arrival, service, c_value);
		fprintf(outfile, "Mean interarrival time%11.3f minutes\n\n", mean_interarrival);
		fprintf(outfile, "Mean service time%16.3f minutes\n\n", mean_service);
		if(terminating_condition == 'p')
		{
			fprintf(outfile, "Number of customers required to finish%14d\n\n", num_delays_required);
		}
		else
		{
			fprintf(outfile, "Time required to finish %17.3f\n\n", time_required);
		}
		fflush(stdout);
		/* Initialize the simulation. */
		initialize();

		/* Run the simulation while more delays are still needed. */
		if(terminating_condition == 'p')
		{
			while (1) {
				/* Determine the next event. */
				timing();
				/* Update time-average statistical accumulators. */

				update_time_avg_stats();
				/* Invoke the appropriate event function. */
				if(num_custs_delayed < num_delays_required || next_event_type == 1)
				{
					switch (next_event_type) {
					case 1:
						arrive();
						break;
					case 2:
						depart();
						break;
					}
				}
				else
				{
					break;
				}
			}
		}
		else
		{
			while (1) {
				/* Determine the next event. */

				timing();
				if(sim_time > time_required)
				{
					sim_time = time_required;
				}
				update_time_avg_stats();
				/* Update time-average statistical accumulators. */
				if(sim_time < time_required) {


					/* Invoke the appropriate event function. */

					switch (next_event_type) {
					case 1:
						arrive();
						break;
					case 2:
						depart();
						break;
					}
				}
				else
				{
					num_custs_delayed--;
					break;
				}
			}
		}

		/* Invoke the report generator and end the simulation. */

		report();
	}
	fclose(outfile);

	return 0;
}


void initialize(void)  /* Initialization function. */
{
	/* Initialize the simulation clock. */
	int i;
	sim_time = 0.0;

	/* Initialize the state variables. */

	server_status   = IDLE;
	num_in_q        = 0;
	time_last_event = 0.0;

	/* Initialize the statistical counters. */
	job_gt_45          = 0;
	num_custs_delayed  = 0;
	num_being_served   = 0;
	*seed1 			   = 443952721;
	*seed2 			   = 1848090842;
	total_of_delays    = 0.0;
	area_num_in_q      = 0.0;
	area_server_status = 0.0;
	time_q_gt1		   = 0.0;
	/* Initialize event list.  Since no customers are present, the departure
       (service completion) event is eliminated from consideration. */

	time_next_event[1] = sim_time + expon(mean_interarrival, seed1);
	time_next_event[2] = 1.0e+30;
	for(i = 0; i < Q_LIMIT; i++)
	{
		service_times[i] = 0;
	}
}


void timing(void)  /* Timing function. */
{
	int   i;
	float min_time_next_event = 1.0e+29;

	next_event_type = 0;

	/* Determine the event type of the next event to occur. */

	for (i = 1; i <= num_events; ++i)
		if (time_next_event[i] < min_time_next_event) {
			min_time_next_event = time_next_event[i];
			next_event_type     = i;
		}
	printf("Event = %d, Time = %f\n", next_event_type, min_time_next_event);
	/* Check to see whether the event list is empty. */

	if (next_event_type == 0) {

		/* The event list is empty, so stop the simulation. */

		fprintf(outfile, "\nEvent list empty at time %f", sim_time);
		exit(1);
	}

	/* The event list is not empty, so advance the simulation clock. */
	sim_time = min_time_next_event;
}


void arrive(void)  /* Arrival event function. */
{
	float delay;

	/* Schedule next arrival. */
	if(arrival == 'M')
	{
		time_next_event[1] = sim_time + expon(mean_interarrival, seed1);
	}
	else if(arrival == 'D')
	{
		time_next_event[1] = sim_time + mean_interarrival;
	}
	else if(arrival == 'U')
	{
		time_next_event[1] = sim_time + randomvar(seed1) * mean_interarrival;
	}
	/* Check to see whether server is busy. */

	if (num_being_served < c_value || c_value == 0) {

		/* Server is idle, so arriving customer has a delay of zero.  (The
           following two statements are for program clarity and do not affect
           the results of the simulation.) */

		delay            = 0.0;
		total_of_delays += delay;

		/* Increment the number of customers delayed, and make server busy. */
		/* Check to see whether an overflow condition exists. */


		num_being_served++;
		if (num_being_served > Q_LIMIT) {

			/* The queue has overflowed, so stop the simulation. */

			fprintf(outfile, "\nOverflow of the array time_arrival at");
			fprintf(outfile, " time %f", sim_time);
			exit(2);
		}
		++num_custs_delayed;

		/* Schedule a departure (service completion). */
		if(service == 'M')
		{
			time_service[num_being_served] = sim_time + expon(mean_interarrival, seed2);
		}
		else if(service == 'D')
		{
			time_service[num_being_served] = sim_time + mean_interarrival;
		}
		else if(service == 'U')
		{
			time_service[num_being_served] = sim_time + randomvar(seed2)* mean_interarrival;
		}
		if(time_service[num_being_served] - sim_time > 4.5)
		{
			if(time_required > time_next_event[2])
			{
				job_gt_45++;
			}
			else if(time_required < 0)
			{
				job_gt_45++;
			}
		}
		if(num_being_served == 1)
		{
			int i;
			time_next_event[2] = time_service[1];
			for (i = 1; i <= num_being_served; ++i)
				time_arrival[i] = time_arrival[i + 1];
		}
	}
}


void depart(void)  /* Departure event function. */
{
	/* The queue is empty so make the server idle and eliminate the
       departure (service completion) event from consideration. */
		num_being_served--;
		num_custs_delayed++;
		if(num_being_served > 0)
		{
			int i;
			time_next_event[2] = time_service[1];
			for (i = 1; i <= num_being_served; ++i)
				time_arrival[i] = time_arrival[i + 1];
		}
		else
		{
			time_next_event[2] = 1.0e+30;
		}

}


void report(void)  /* Report generator function. */
{
	/* Compute and write estimates of desired measures of performance. */
	float server_util = area_server_status/sim_time;
	float avg_in_system = (area_server_status)/sim_time;
	fprintf(outfile, "Number of customers served%7.1d\n\n", num_custs_delayed);
	fprintf(outfile, "Average number in system%10.3f\n\n", avg_in_system);
	fprintf(outfile, "Fraction of Jobs that spent more than 4.5 minutes in the system%10.3f\n\n", (float)job_gt_45/(float)num_custs_delayed);
	fprintf(outfile, "Server utilization%15.3f\n\n", area_server_status / sim_time);
	fprintf(outfile, "Time simulation ended%12.3f minutes\n\n", sim_time);

}


void update_time_avg_stats(void)  /* Update area accumulators for time-average
                                     statistics. */
{
	float time_since_last_event;

	/* Compute time since last event, and update last-event-time marker. */

	time_since_last_event = sim_time - time_last_event;
	time_last_event       = sim_time;

	/* Update area under server-busy indicator function. */

	area_server_status += num_being_served * time_since_last_event;
}


float expon(float mean, double *ex_seed)  /* Exponential variate generation function. */
{
	/* Return an exponential random variate with mean "mean". */

	return -mean * log(randomvar(ex_seed));
}

float randomvar(double *rand_seed)
{      
	hi=floor(*rand_seed/q);
	lo=*rand_seed-q*hi;
	test=a*lo-r*hi;
	if (test>0.0)
		*rand_seed=test;
	else
		*rand_seed=test+m;
	my_random=*rand_seed/m;
	return my_random;
}
